﻿using System;
using GeoAPI.Geometries;

namespace NetTopologySuite.Algorithm
{
	public class Centroid
	{
		public static Coordinate GetCentroid(IGeometry geom)
		{
			return new Centroid(geom).GetCentroid();
		}

		public Centroid(IGeometry geom)
		{
			this._areaBasePt = null;
			this.Add(geom);
		}

		private void Add(IGeometry geom)
		{
			if (geom.IsEmpty)
			{
				return;
			}
			if (geom is IPoint)
			{
				this.AddPoint(geom.Coordinate);
				return;
			}
			if (geom is ILineString)
			{
				this.AddLineSegments(geom.Coordinates);
				return;
			}
			if (geom is IPolygon)
			{
				IPolygon poly = (IPolygon)geom;
				this.Add(poly);
				return;
			}
			if (geom is IGeometryCollection)
			{
				IGeometryCollection geometryCollection = (IGeometryCollection)geom;
				for (int i = 0; i < geometryCollection.NumGeometries; i++)
				{
					this.Add(geometryCollection.GetGeometryN(i));
				}
			}
		}

		public Coordinate GetCentroid()
		{
			Coordinate coordinate = new Coordinate();
			if (System.Math.Abs(this._areasum2) > 0.0)
			{
				coordinate.X = this._cg3.X / 3.0 / this._areasum2;
				coordinate.Y = this._cg3.Y / 3.0 / this._areasum2;
			}
			else if (this._totalLength > 0.0)
			{
				coordinate.X = this._lineCentSum.X / this._totalLength;
				coordinate.Y = this._lineCentSum.Y / this._totalLength;
			}
			else
			{
				if (this._ptCount <= 0)
				{
					return null;
				}
				coordinate.X = this._ptCentSum.X / (double)this._ptCount;
				coordinate.Y = this._ptCentSum.Y / (double)this._ptCount;
			}
			return coordinate;
		}

		private void SetBasePoint(Coordinate basePt)
		{
			if (this._areaBasePt == null)
			{
				this._areaBasePt = basePt;
			}
		}

		private void Add(IPolygon poly)
		{
			this.AddShell(poly.ExteriorRing.Coordinates);
			for (int i = 0; i < poly.NumInteriorRings; i++)
			{
				this.AddHole(poly.GetInteriorRingN(i).Coordinates);
			}
		}

		private void AddShell(Coordinate[] pts)
		{
			if (pts.Length != 0)
			{
				this.SetBasePoint(pts[0]);
			}
			bool isPositiveArea = !CGAlgorithms.IsCCW(pts);
			for (int i = 0; i < pts.Length - 1; i++)
			{
				this.AddTriangle(this._areaBasePt, pts[i], pts[i + 1], isPositiveArea);
			}
			this.AddLineSegments(pts);
		}

		private void AddHole(Coordinate[] pts)
		{
			bool isPositiveArea = CGAlgorithms.IsCCW(pts);
			for (int i = 0; i < pts.Length - 1; i++)
			{
				this.AddTriangle(this._areaBasePt, pts[i], pts[i + 1], isPositiveArea);
			}
			this.AddLineSegments(pts);
		}

		private void AddTriangle(Coordinate p0, Coordinate p1, Coordinate p2, bool isPositiveArea)
		{
			double num = isPositiveArea ? 1.0 : -1.0;
			Centroid.Centroid3(p0, p1, p2, this._triangleCent3);
			double num2 = Centroid.Area2(p0, p1, p2);
			this._cg3.X += num * num2 * this._triangleCent3.X;
			this._cg3.Y += num * num2 * this._triangleCent3.Y;
			this._areasum2 += num * num2;
		}

		private static void Centroid3(Coordinate p1, Coordinate p2, Coordinate p3, Coordinate c)
		{
			c.X = p1.X + p2.X + p3.X;
			c.Y = p1.Y + p2.Y + p3.Y;
		}

		private static double Area2(Coordinate p1, Coordinate p2, Coordinate p3)
		{
			return (p2.X - p1.X) * (p3.Y - p1.Y) - (p3.X - p1.X) * (p2.Y - p1.Y);
		}

		private void AddLineSegments(Coordinate[] pts)
		{
			double num = 0.0;
			for (int i = 0; i < pts.Length - 1; i++)
			{
				double num2 = pts[i].Distance(pts[i + 1]);
				if (num2 != 0.0)
				{
					num += num2;
					double num3 = (pts[i].X + pts[i + 1].X) / 2.0;
					this._lineCentSum.X += num2 * num3;
					double num4 = (pts[i].Y + pts[i + 1].Y) / 2.0;
					this._lineCentSum.Y += num2 * num4;
				}
			}
			this._totalLength += num;
			if (num == 0.0 && pts.Length != 0)
			{
				this.AddPoint(pts[0]);
			}
		}

		private void AddPoint(Coordinate pt)
		{
			this._ptCount++;
			this._ptCentSum.X += pt.X;
			this._ptCentSum.Y += pt.Y;
		}

		private Coordinate _areaBasePt;

		private readonly Coordinate _triangleCent3 = new Coordinate();

		private double _areasum2;

		private readonly Coordinate _cg3 = new Coordinate();

		private readonly Coordinate _lineCentSum = new Coordinate();

		private double _totalLength;

		private int _ptCount;

		private readonly Coordinate _ptCentSum = new Coordinate();
	}
}
